﻿#include "record_proc.h"
#include "record_set.h"
#include "cross_com.h"
#include <fcntl.h>
#ifdef _SIMULATE_RECORD_
#include "MultiCHData.h"
#endif

int CRecord1CHProc::PREPARE_SECS = 5;
CRecord1CHProc::CRecord1CHProc(uint32_t channel, DATA_SOURCE_ID dataID)
{
	m_bOpenFile = false;				//是否打开文件
	m_bStart    = false;				//是否已经写了开始
	m_bSleep    = false;				//是否处于休眠状态

	m_diskGroup = 0;					//磁盘分组序号
	m_curDiskIdx = 0;					//磁盘序号

	m_bLockRecord	 = false;			//是否锁定录像，锁定状态时，只能手动开启或停止
	m_bRecycleRecord = true;			//是否覆盖录像
	m_bEnablePre     = true;			//是否开启预备录像
	m_bManualRec     = false;			//当前正处于何种录像类型

	m_channel	   = (uint16_t)channel;	//通道编码
	m_fileLength   = 0;

	memset(&m_recSegInfo,0,sizeof(REC_FILE_INFO));

	m_recSegInfo.channel    = (uint8_t) m_channel;
	//m_recSegInfo.recordType = RECORD_NONE;
	m_recSegInfo.status	    = RECORD_FILE_USED;
	m_recSegInfo.diskIndex  = 0;
	m_recSegInfo.dataSource = dataID;
	m_recSegInfo.enableText = false;
	m_recSegInfo.segIndex   = 0;
	m_startTime    = 0;
	m_stopChkTime  = 0;
	m_fileIndex    = 0;
    m_checkTm      = 0;
	m_lastFrmTIme  = 0;
	m_bPrepareData = false;
	m_rate         = 0;

	m_dataSourceID = DATA_SOURCE_RECORD;

	m_VHeight      = 0;             //视频高
	m_vFrameRate   = 30;

	m_recordFileType = RECORD_FILE_AVI;
	CRecWriteFactory recordWriteFac;

	m_pReoWriter  = recordWriteFac.GetRecorderInstance(m_recordFileType,channel);

	m_destFile = "/mnt/NOR/00000.avi";
	m_destDiskName = "dev/mmcblk0";
	m_vFrameCnt  = 0;
	m_externInfo.vWidth = 960;
	m_externInfo.vHeight= 576;
	m_pRecIndexSet = &g_pFileMangerArr;
}

CRecord1CHProc::~CRecord1CHProc()
{

}


int CRecord1CHProc::Init(int recordFileType)
{
	if(m_bStart)
	{
		return ERR_INIT_RECORDING;
	}

	if(recordFileType != m_recordFileType)
	{   //切换到其它的录像类型文件
		CRecWriteFactory recordWriteFac;
		m_pReoWriter->Close();
		delete m_pReoWriter;
		m_pReoWriter = recordWriteFac.GetRecorderInstance(recordFileType,m_channel);
	}

	m_recordFileType = recordFileType;
	return 0;
}

int CRecord1CHProc::StartRecord(int type,int64_t time, char *pText, uint32_t textLen)
{
	uint32_t fileLen = DEF_RECORD_SIZE;
	int ret        = 0;
	int preRecSize = 0; //写入了预录像大小
	time_t recTime = 0;

	m_startTime = time;
	recTime = (time_t)(m_startTime/1000);
	if(m_pReoWriter->IsOverMaxSeg())
	{
		TRACE_ERR("Over max segment %d.\n",STFS_MAX_SEG_NUM);
		m_pReoWriter->Close();
		m_bOpenFile = false;
		m_fileLength = 0;
	}


	if(!m_bOpenFile) //获取到下一个可写索引
	{
		ret = m_pRecIndexSet->GetNextRecIndex(m_channel,m_curDiskIdx,m_destFile,m_fileIndex,fileLen,recTime,type);
		if(ERR_SPACE_NOT_ENOUGH  == ret)
		{
			TRACE("ch%d Disk spcae not enough \n",m_channel);
			return ret;
		}

		if(ret < 0)
		{
			if(ERR_NO_DEVICE == ret)
			{
				fprintf(stderr,"Device is lost.\n");
				return ret;
			}

			if(m_bRecycleRecord && (ERR_INDEX_NORECIDX == ret))
			{
				ret = m_pRecIndexSet->GetCycleIndex(m_channel,m_destFile,m_curDiskIdx,m_fileIndex,fileLen,recTime);
			}

			CRecordSet::m_procBIT |= CRecordSet::PROC_DISK_FULL;
			if( ret < 0)
			{   //磁盘满的回调
				g_pRecSet->CallbackRecStatus(m_channel,RECORD_NONE);
				return ret;
			}
			fprintf(stderr,"fileName %s Get reCycle m_diskIndex %d fileindex %d .\n",m_destFile.c_str(),m_curDiskIdx,ret);
		}

		fprintf(stderr,"CH %02d Get next index diskIndex %d fileindex %d Dev %s.\n",m_channel,m_curDiskIdx,m_fileIndex,m_destFile.c_str());
		PrintTimeMs(m_startTime,0,"record time");
		CreateFileExternInfo(m_externInfo);
	}


	//通过m_diskIndex编号获取设备名称
	char * pdev = m_pRecIndexSet->GetDiskName(m_curDiskIdx);
	if(pdev != NULL)
	{
		m_pReoWriter->SetStorageName(pdev);
	}
	else
	{
		m_pReoWriter->SetStorageName((char *)m_destDiskName.c_str());
	}
STRAT_REC:
	CCross_LockAuto tempLock(&m_lock);
	ret = m_pReoWriter->Open(m_destFile.c_str(),m_fileIndex,fileLen,m_startTime,&m_externInfo);

	if( 0 == ret ||		//打开成功
	    ERR_FILE_OPENED == ret )
	{	//ret = 0 　　　 为每一次打开文件
		//ERR_FILE_OPEND　文件已经打开
		//预录像处理，一般都提前5秒时间
		if(ERR_FILE_OPENED == ret )
		{
			m_recSegInfo.tmEnd  = m_recSegInfo.tmStart = time;
			m_pReoWriter->Resume(m_recSegInfo);
		}
		//当处理完预录像后，就应该切换成正常的录像缓冲模式

		m_pRecIndexSet->RegisterRecWriter(m_curDiskIdx,m_channel,m_pReoWriter);
	}

	//切换成录像模式
	if(0 != ret && ERR_FILE_OPENED != ret)
	{
		//TRACE_ERR("Open file fail, CH %d dev %s index %d, fileLen %d.\n",m_channel,destDev.c_str(),m_fileIndex,fileLen);
		if(ERR_FILE_OPENED != ret)
		{
			m_bStart = false;
		}
		return ret;
	}

	//处理一下预录像数据
    memset(&m_recSegInfo,0,sizeof(REC_FILE_INFO));
    m_recSegInfo.recordType = type;
    if(ret != ERR_FILE_OPENED)
    {
        m_recSegInfo.procstep = REC_PROCESS_OPEN;
    }
    m_recSegInfo.tmEnd      = m_recSegInfo.tmStart = time;
    m_recSegInfo.dataSource = m_dataSourceID;
    m_recSegInfo.status     = RECORD_FILE_USED|RECORD_FILE_RECORDING ;
    m_recSegInfo.channel    = (uint8_t)m_channel;
    m_recSegInfo.indexNo    = m_fileIndex;
	m_recSegInfo.diskIndex  = (uint8_t)m_curDiskIdx;
	m_recSegInfo.timeZone   = GetTimeZone();

	m_lastFrmTIme = time;
    m_bOpenFile  = true;
    m_bStart	 = true;
	m_vFrameCnt   = 0;
    //开始了预录像的话，需要把预录像的大小加入进去
    if(preRecSize > 0)
    {
        m_fileLength = preRecSize;
    }
    else
    {
        m_fileLength = 0;
    }

    ret = m_pRecIndexSet->RecordStart(m_recSegInfo);
    if(0 != ret)
    {
        if(ERR_OVER_MAXRECSEGS == ret)
        {
            m_pReoWriter->Close();
            m_bOpenFile  = false;
            m_fileLength = 0;
        }
        TRACE("Write record start fail.\n");
        return ret;
    }
    else
    {	//注册文件写入对象
        m_pRecIndexSet->RegisterRecWriter(m_curDiskIdx,m_channel,m_pReoWriter);
    }

	m_recordSN = m_recSegInfo.recordSN;     //录像块序列号
    if(m_recSegInfo.status & RECORD_FILE_MARGED)
    {  //出现录像段合并，间隔一秒中会被合并。
		TRACE("Total Len %d preRecSize %d lastSegment Len %d .\n ",m_fileLength,preRecSize,m_recSegInfo.length);
		m_fileLength += m_recSegInfo.length;
		PrintTimeMs(m_startTime,0,"Start Time");
		PrintTimeMs(m_recSegInfo.tmStart,0,"Start Time");
		TRACE("Merge Record %d .\n",m_recSegInfo.length);
        m_recSegInfo.status &= ~RECORD_FILE_MARGED;
    }
	
	g_pRecSet->CallbackRecStatus(m_channel, m_recSegInfo.recordType);

	return type ;
}
//寻找修复过的录像来进行录像．节约利用磁盘空间．
//对于时间不符合
int CRecord1CHProc::RecordByRepairFile(int type, int64_t time, char *pText, uint32_t textLen)
{
	int ret = 0;
	uint32_t fileLen = DEF_RECORD_SIZE;

// 	ret = m_pRecIndexSet->GetUnfinishedIndex(m_channel,m_recSegInfo,fileLen);
// 	if(ret < 0)
// 	{
// 		return ret;
// 	}
//
// 	TRACE("Seginfo channel %d index %d filelen %d .\n",
// 		   m_recSegInfo.channel,m_recSegInfo.indexNo,fileLen);
// 	PrintTimeMs(m_recSegInfo.tmStart,m_recSegInfo.tmEnd,"SegInfo time");

// 	if( (m_startTime < m_recSegInfo.tmStart)||                 //时间倒回去．
// 		(m_startTime - m_recSegInfo.tmStart) >= STFS_ONEDAY_MS) //时间出现跨天
// 	{
// 		TRACE("This record file cannot record.\n");
// 		PrintTimeMs(m_recSegInfo.tmStart,m_recSegInfo.tmEnd,"repair reocrd time");
// 		PrintTimeMs(m_startTime,0,"start reocrd Time");
//
// 		return ERR_INDEX_NOTFIT;
// 	}
	CCross_LockAuto tempLock(&m_lock);;
	m_recSegInfo.dataSource = DATA_SOURCE_RECORD;
	m_recSegInfo.tmStart    = time;
	m_recSegInfo.timeZone   = GetTimeZone();
	m_fileIndex = m_recSegInfo.indexNo;
	//通过m_diskIndex编号获取设备名称

// 	ret = m_pRecIndexSet->RecordReInit(m_recSegInfo);
// 	ret = m_pReoWriter->OpenRepairFile(m_recSegInfo,fileLen,time);
// 	if(ret < 0)
// 	{
// 		TRACE("Open repair File fail.\n");
// 		return ret;
// 	}

	if(m_bPrepareData)
	{
		m_bPrepareData = false;
	}

	m_pRecIndexSet->RegisterRecWriter(m_recSegInfo.diskIndex,m_channel,m_pReoWriter);
	//处理一下预录像数据
    if(0 == time )
    {
        TRACE("Error time %lld.\n",m_startTime);
    }

	m_recSegInfo.recordType = type;
	m_recSegInfo.tmEnd      = m_recSegInfo.tmStart = time;
	m_recSegInfo.dataSource = DATA_SOURCE_RECORD;
	m_recSegInfo.status     = RECORD_FILE_USED|RECORD_FILE_RECORDING ;
	m_recSegInfo.channel    = (uint8_t)m_channel;
	m_recSegInfo.indexNo    = m_fileIndex;
	m_recSegInfo.procstep = REC_PROCESS_MULSEG_START;

	m_bStart	 = true;
	m_fileLength = 0;

	ret = m_pRecIndexSet->RecordStart(m_recSegInfo);
	if(0 != ret)
	{
		TRACE("Write record start fail.\n");
		return ret;
	}

	m_startTime = time;
	return type;
}
//停止录像，产生录像段
int CRecord1CHProc::StopRecord(int type,bool bCloseFile,int newType)
{
	int ret = 0;
	if(!m_bStart)
	{
		if(bCloseFile && m_pReoWriter->IsOpened())
		{
			m_pReoWriter->Close();
			m_bOpenFile  = false;
		}
		m_fileLength = 0;
		//TRACE("CH %02d This channel no start record.\n",m_channel);
		return 0;
	}

	CCross_LockAuto tempLock(&m_lock);

	if(m_pReoWriter->GetFreeSize() < STFS_REC_LEAST_SPACE)
	{   //剩余空间过小时，同样把文件关闭掉
		m_recSegInfo.procstep = REC_PROCESS_CLOSED;
		m_bOpenFile  = false;
		bCloseFile   = true;
	}

	if(bCloseFile)
	{
		m_recSegInfo.procstep = REC_PROCESS_CLOSED;
     //   m_pReoWriter->Close();
		m_bOpenFile  = false;
	}
	else
	{
		m_pReoWriter->PauseRec(m_recSegInfo);
		m_bOpenFile = true;
	}
	m_recSegInfo.recordType = type;
	if( 0 != m_fileLength)
	{
	//	TRACE("CH%02d over file size %d record space %d.\n",m_channel,m_fileLength,DEF_RECORD_SIZE);
		//CROSS_ASSERT(m_pReoWriter->GetLastFrmTime()  >= m_startTime);
	}

	if(m_lastFrmTIme == 0)
	{
		GetDvrCurTime(&m_lastFrmTIme);
		m_lastFrmTIme -= 500;
	}

	if( 0 == m_recSegInfo.tmStart)
	{
		m_recSegInfo.tmStart = m_pReoWriter->GetFileHead()->startTime;
	}

    m_recSegInfo.tmEnd  = m_lastFrmTIme;//m_pReoWriter->GetLastFrmTime();//关键的获取最后一帧时间
	m_recSegInfo.length = m_fileLength;
	if(m_fileLength >= DEF_RECORD_SIZE )
	{
		TRACE("CH%02d over file size %d record space %d.\n",m_channel,m_fileLength,DEF_RECORD_SIZE);
		//CROSS_ASSERT(m_fileLength <= DEF_RECORD_SIZE);
		m_recSegInfo.length = m_pReoWriter->GetCurSegSize();
	}

	m_recSegInfo.status   &= (~RECORD_FILE_RECORDING);
	m_recSegInfo.endOffset = m_pReoWriter->GetLastOffset();


	if(m_recSegInfo.tmEnd   < m_recSegInfo.tmStart)
	{
		m_recSegInfo.tmEnd  = m_recSegInfo.tmStart;
	}

	TRACE("CH%02d  irec info ch %d indexNO %d len %d m_bOpenFile %d.\n",m_channel, m_recSegInfo.channel,m_recSegInfo.indexNo,m_recSegInfo.length,m_bOpenFile);
	m_recSegInfo.channel = (uint8_t)m_channel;
    ret = m_pRecIndexSet->RecordEnd(m_recSegInfo);

    if(ERR_INDEX_OVERSEGNUM == ret)
    {   //已经不能够存储多余的录像段
        bCloseFile   = true;
        m_recSegInfo.procstep = REC_PROCESS_CLOSED;;
		ret = m_pReoWriter->Close();
		m_bOpenFile  = false;

		TRACE("CH%02d index NO %d over ERR_INDEX_OVERSEGNUM",m_channel, m_recSegInfo.indexNo);
    }
	TRACE("CH%02d index NO rec info ch %d %d Record End type %d len %d m_bOpenFile %d.\n",m_channel, m_recSegInfo.channel,m_recSegInfo.indexNo,m_recSegInfo.recordType,m_recSegInfo.length,m_bOpenFile);
	PrintTimeMs(m_recSegInfo.tmStart,m_recSegInfo.tmEnd,"Record End");

	if(bCloseFile)
	{
		m_pRecIndexSet->DestroyRecWriter(m_curDiskIdx,m_channel);
		m_recSegInfo.segIndex = 0;
		m_pReoWriter->Close();
	}

	m_fileLength = 0;
	m_bStart = false;
	m_rate   = 0;
	m_VHeight= 0;
	
	g_pRecSet->CallbackRecStatus(m_channel,RECORD_NONE);

	return ret;
}

int CRecord1CHProc::InterruptRecord(char * device)
{
	int ret = 0;
	//判断是否为同一个磁盘
	if(0 != strcmp(m_pReoWriter->GetRecFile()->get_device_name(),device))
	{
		TRACE_ERR("Not same device %s \n",device);
		return ERR_NO_DEVICE;
	}

	CCross_LockAuto tempLock(&m_lock);
	m_pReoWriter->Close();
	m_fileLength = 0;
	m_bStart = false;
	m_rate   = 0;

	return ret;
}

void CRecord1CHProc::Continue()	//继续录像
{
	m_bSleep = false;

    struct timeval tv;			//记录时间
	gettimeofday(&tv,NULL);
	m_startTime = (int64_t)tv.tv_sec *1000 + ((int64_t)tv.tv_usec)/1000;

	m_recSegInfo.length = m_fileLength = 0;	//数据长度清零
	m_recSegInfo.tmStart= m_startTime;

    m_pRecIndexSet->RecordStart(m_recSegInfo);
}

void CRecord1CHProc::Pause()		//暂停录像
{
	m_bSleep = true;

	//更新最后一次信息，最后再写入
	if(m_recSegInfo.recordType > RECORD_MANUAL)
	{
		TRACE_ERR("Other higher record type %d.\n",m_recSegInfo.recordType);
		return;
	}
	m_recSegInfo.recordType = RECORD_MANUAL;
	m_recSegInfo.tmEnd  = m_pReoWriter->GetLastFrmTime();
	m_recSegInfo.length = m_fileLength;
	m_recSegInfo.status &= (~RECORD_FILE_RECORDING);

	m_pRecIndexSet->RecordEnd(m_recSegInfo);
}

void CRecord1CHProc::Close()
{
	m_pReoWriter->Close();
	m_pRecIndexSet->DestroyRecWriter(m_curDiskIdx,m_channel);
	m_bOpenFile  = false;
	m_fileLength = 0;
	m_bStart = false;
}

void CRecord1CHProc::SetDataSource(int dataSourceID)
{
	m_dataSourceID = dataSourceID;
}


//录像写入
int CRecord1CHProc::WriteFrameData(uchar_t * frameBuf, int size)
{
	int ret	= 0;
	//过滤一下时间对不对
	LHN_FRAME_H* pFHead = (LHN_FRAME_H*)frameBuf;
	int64_t tmpPts = pFHead->pts;

// 	if( 0 == pFHead->pts )
// 	{
// 		TRACE_ERR("CH%02d frame Type %d data Len %d pts %lld .\n",
// 			        pFHead->channel,pFHead->frameType,pFHead->dataLen,pFHead->pts);
// 	//	CROSS_ASSERT(pFHead->pts != 0);
// 		return 0;
// 	}


	ret = m_pReoWriter->WriteFrame(frameBuf,size);
	if(ret < 0)
	{	//写入失败
		//TRACE_ERR("CH%02d file index %d Write frame data fail. ret %d \n",m_channel,m_fileIndex,ret);
		return ret;
	}

#ifdef _SIMULATE_RECORD_
	if(AFrame != pFHead->frameType)
	{
		m_vFrameCnt++;
		if(m_vFrameCnt >= 30)
		{
			STFS_GPS_IDX gpsInfo;
			CreateGpsInfo(gpsInfo);
			WriteGPSInfo((uchar_t *)&gpsInfo,gpsInfo.idxSize);
			m_vFrameCnt = 0;
		}
	}
#endif

	m_lastFrmTIme = tmpPts;
	m_fileLength += SIZE_ROUND_UP(pFHead->dataLen,2) + 4; //
	
	return size;
}

int CRecord1CHProc::WriteGPSInfo(uchar_t * buf, int size)
{
	int ret = m_pReoWriter->WriteGPSInfo(buf,size);
	if(ret < 0)
	{	//写入失败
		//TRACE_ERR("CH%02d file index %d Write frame data fail. ret %d \n",m_channel,m_fileIndex,ret);
		return ret;
	}
	return size;
}

int CRecord1CHProc::WritePicFile(uchar_t * frameBuf, int size)
{
	int fd = -1;
	string file_name;
	int ret = 0;
	uchar_t * pData = frameBuf + BASE_FRAME_HEAD_LEN;
	uint32_t  dest_size = size - BASE_FRAME_HEAD_LEN;
	time_t curTm = 0;

	time(&curTm);

	//获取图片文件名
	ret = m_pRecIndexSet->GetNextPic(file_name,curTm,m_channel);

	//打开文件
	fd = open(file_name.c_str(),O_RDWR|O_CREAT);
	if(fd == -1)
	{
		return 0;
	}
	//开始写文件
	ret = write(fd,pData,dest_size);

	//关闭文件
	close(fd);

	return 0;
}

int CRecord1CHProc::CreateGpsInfo(STFS_GPS_IDX & gpsInfo)
{
	static int GPS_IDX_SIZE = sizeof(STFS_GPS_IDX);
	time_t curTm = 0;

	gpsInfo.gpsValid   = true;
	gpsInfo.frameType  = GPSInfo;
	gpsInfo.idxSize    = GPS_IDX_SIZE;
	gpsInfo.magic      = STFS_GPS_IDX_SIG;

	srand((int)time(NULL));
	time(&curTm); //(time_t*)&gpsInfo.tmSec);

	gpsInfo.tmMS = ((int64_t) curTm)* 1000;
	gpsInfo.speed      = 30 + Random(0,30);
	gpsInfo.latitude   = 100 + Random(-1000,1000);
	gpsInfo.longitude  = 100 + Random(-1000,1000);

	return 0;
}

bool CRecord1CHProc::IsNeedStop()
{
	int64_t  curTime = GetmSecond();
	int64_t  dfftime = ABS(curTime - m_pReoWriter->GetLastFrmTime());

	if(curTime < m_pReoWriter->GetLastFrmTime())
	{
		return false;
	}

	if( dfftime > STFS_CREATESEG_DIFFTM_MS &&
		m_stopChkTime != m_pReoWriter->GetLastFrmTime() )
	{
		TRACE_ERR("CH%02d frame data is lost dfftime %lld (ms).\n",m_channel,dfftime);
		TRACE_ERR("last frame time");PrintTimeMs(m_pReoWriter->GetLastFrmTime());
		TRACE_ERR("Cur frame time");PrintTimeMs(curTime);
	    m_stopChkTime = m_pReoWriter->GetLastFrmTime();
		return true;
	}

	return false;
}

int CRecord1CHProc::AnalyzeKeyFrm(const void * buf,uint32_t size)
{
	CFrmUtility frmTool;
	int vWidth = 0;
	int vHeight = 0;
	if(size < 100)
	{  //视频关键帧没有太小
		return ERR_DATA_SIZE;
	}

#if 0
	m_pReoWriter->SaveSpsPps((uchar_t*)buf,size);
	frmTool.GetResolution(vWidth,vHeight,buf,size);
#endif
//	m_externInfo.vWidth = 960;
//	m_externInfo.vHeight= 576;
	m_externInfo.vWidth = (uint16_t)vWidth;
	m_externInfo.vHeight= (uint16_t)vHeight;

	return 0;
}

bool  CRecord1CHProc::GetCheckTime()
{
    if(m_pReoWriter->GetLastFrmTime() /1000 - m_checkTm > (45 + m_channel))
    {
        time(&m_checkTm);
        return true;
    }
    else
    {
        return false;
    }
}

void  CRecord1CHProc::CreateFileExternInfo(STFS_RECINFO_EXTERN & m_externInfo)
{
	m_externInfo.recordSN   = m_pRecIndexSet->GetNextRecordSN();
    sprintf(m_externInfo.deviceName,"%s",DEVICE_VER);
	sprintf(m_externInfo.softVersion,"%s",INTERNAL_VER);
}

void CRecord1CHProc::SetVideoParm(int width,int height,int frame_rate)
{
	m_externInfo.vWidth = width;
	m_externInfo.vHeight = height;
	m_externInfo.vRate  = frame_rate;
}

void CRecord1CHProc::SetAudioParm(int audio_channels, long rate, int bits, int format)
{
	m_externInfo.audio_channels     = audio_channels;
	m_externInfo.audio_bit     = bits;
	m_externInfo.audio_rate    = rate;
	m_externInfo.audio_fmt   = format;
}
